package com.lzh.file.service.impl;

import com.google.common.collect.Lists;
import com.lzh.file.entity.FileInfo;
import com.lzh.file.mapper.FileInfoMapper;
import com.lzh.file.service.IFileService;
import com.lzh.file.store.FileStoreApi;
import com.lzh.runke.core.entity.spec.BaseSpec;
import com.lzh.runke.core.service.impl.BaseServiceImpl;
import com.lzh.runke.core.web_base.Result;
import com.lzh.runke.core.web_base.constant.StringConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.util.Collection;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class FileServiceImpl extends BaseServiceImpl<FileInfoMapper, FileInfo, BaseSpec> implements IFileService {

    @Resource
    private FileInfoMapper fileInfoMapper;

    @Resource(name = "FileStoreFileSystem")
    private FileStoreApi fileStoreApi;

    /**
     * 上传文件
     * @param file
     * @return
     */
    @Override
    @Transactional
    public Result<FileInfo> upload(File file) {
        String filename = fileStoreApi.save(file);
        if (null == filename) {
            return Result.<FileInfo>builder().success(false).message("文件上传失败").build();
        }

        FileInfo fileInfo = new FileInfo(filename);
        // 设置原始文件名
        fileInfo.setOriginalName(file.getName());
        // 设置上传时间
        fileInfo.setUploadDate(new Date());
        // 设置文件大小
        fileInfo.setFileSize(file.length());

        // 保存文件信息
        int res = fileInfoMapper.insert(fileInfo);
        if (res < 1) {
            // 文件信息保存失败，删除对应文件
            fileStoreApi.deleteFile(fileInfo.getFilename());
            return Result.<FileInfo>builder().success(false).message("文件信息保存失败").build();
        }

        return Result.<FileInfo>builder().success(true).data(fileInfo).message("文件上传成功").build();
    }

    /**
     * 上传文件
     * @param file
     * @return
     */
    @Override
    @Transactional
    public Result<FileInfo> upload(MultipartFile file) {
        String filename = fileStoreApi.save(file);
        if (null == filename) {
            return Result.<FileInfo>builder().success(false).message("文件上传失败").build();
        }

        FileInfo fileInfo = new FileInfo(filename);
        // 设置原始文件名
        fileInfo.setOriginalName(file.getOriginalFilename());
        // 设置上传时间
        fileInfo.setUploadDate(new Date());
        // 设置文件大小
        fileInfo.setFileSize(file.getSize());

        // 保存文件信息
        int res = fileInfoMapper.insert(fileInfo);
        if (res < 1) {
            // 文件信息保存失败，删除对应文件
            fileStoreApi.deleteFile(fileInfo.getFilename());
            return Result.<FileInfo>builder().success(false).message("文件信息保存失败").build();
        }

        return Result.<FileInfo>builder().success(true).data(fileInfo).message("文件上传成功").build();
    }

    /**
     * 上传文件(多个)
     * @param files []
     * @return fileInfos Result<List<FileInfo>>
     */
    @Override
    @Transactional
    public Result<List<FileInfo>> upload(MultipartFile[] files) {
        return this.upload(Lists.newArrayList(files));
    }

    /**
     * 上传文件(多个)
     * @param files Collection
     * @return fileInfos Result<List<FileInfo>>
     */
    @Override
    @Transactional
    public Result<List<FileInfo>> upload(Collection<MultipartFile> files) {
        Assert.notEmpty(files, "参数files不能为空");

        final List<FileInfo> fileInfos = Lists.newArrayListWithExpectedSize(files.size());
        files.forEach((file) -> {
            String filename = fileStoreApi.save(file);
            if (null == filename) {
                throw new RuntimeException("文件保存失败");
            }
            FileInfo fileInfo = new FileInfo(filename);
            // 设置原始文件名
            fileInfo.setOriginalName(file.getOriginalFilename());
            // 设置上传时间
            fileInfo.setUploadDate(new Date());
            // 设置文件大小
            fileInfo.setFileSize(file.getSize());
            fileInfos.add(fileInfo);
        });

        // 保存文件信息
        int res = fileInfoMapper.insertBatch(fileInfos);
        if (res < 1) {
            // 文件信息保存失败，删除对应文件
            String[] filenames = fileInfos.stream().map(FileInfo::getFilename).toArray(String[]::new);
            fileStoreApi.deleteFile(filenames);
            return Result.<List<FileInfo>>builder().success(false).message("文件信息保存失败").build();
        }

        return Result.<List<FileInfo>>builder().success(true).data(fileInfos).message("文件上传成功").build();
    }

    /**
     * 根据文件名获取文件信息
     *
     * @param filename
     * @return
     */
    @Override
    public FileInfo findByFilename(String filename) {
        if (StringUtils.isBlank(filename)) {
            return null;
        }
        FileInfo fileInfo = fileInfoMapper.findByFilename(filename);
        return fileInfo;
    }

    /**
     * 根据文件名获取文件信息
     * @param fileInfos
     * @return
     */
    @Override
    public FileInfo[] loadDetailsByFilenames(FileInfo[] fileInfos) {
        if (ArrayUtils.isEmpty(fileInfos)) {
            return null;
        }
        List<FileInfo> fileInfoList = fileInfoMapper.findByFilenames(fileInfos);
        return fileInfoList.toArray(new FileInfo[0]);
    }

    /**
     * 根据文件名更新是否已与业务表关联
     *
     * @param filename
     * @return
     */
    @Override
    @Transactional
    public void relateByFilename(String filename) {
        try {
            fileInfoMapper.relate(getIdByFilename(filename));
        } catch (Exception e) {
            throw new RuntimeException("文件状态修改失败");
        }
    }

    /**
     * 根据文件名更新是否已与业务表关联
     *
     * @param fileInfos
     * @return
     */
    @Override
    @Transactional
    public void relateByFilenames(FileInfo[] fileInfos) {
        if (ArrayUtils.isEmpty(fileInfos)) {
            return;
        }
        try {
            fileInfoMapper.relateBatch(fileInfos);
        } catch (Exception e) {
            throw new RuntimeException("文件状态修改失败");
        }
    }

    /**
     * 根据文件名更新是否已与业务表关联
     *
     * @param filenames
     * @return
     */
    @Override
    @Transactional
    public void relateByFilenames(List<String> filenames) {
        if (CollectionUtils.isEmpty(filenames)) {
            return;
        }
        List<FileInfo> fileInfos = Lists.newArrayListWithExpectedSize(filenames.size());
        filenames.parallelStream().forEach(filename -> fileInfos.add(new FileInfo(filename)));
        relateByFilenames(fileInfos.toArray(new FileInfo[0]));
    }

    /**
     * 删除已上传文件
     *
     * @param filename
     * @return
     */
    @Override
    @Transactional
    public Result<Void> deleteUploaded(String filename) {
        Assert.hasText(filename, "文件名不能为空");
        Boolean deleted = fileStoreApi.deleteFile(filename);
        if (BooleanUtils.isTrue(deleted)) {
            // 删除文件记录
            fileInfoMapper.remove(getIdByFilename(filename));
            return Result.successVoidResult();
        }
        return Result.failureVoidResult("文件删除失败");
    }

    /**
     * 删除已上传文件
     *
     * @param filenames
     * @return
     */
    @Override
    @Transactional
    public Result<Void> deleteUploaded(List<String> filenames) {
        Assert.notNull(filenames, "文件名不能为空");
        filenames.parallelStream().forEach(this::deleteUploaded);
        return Result.successVoidResult();
    }

    /**
     * 文件预览
     *
     * @param filename
     * @return
     */
    @Override
    public Result<byte[]> preview(String filename) {
        byte[] bytes = fileStoreApi.generatePreview(filename);
        return Result.<byte[]>builder().success(true).data(bytes).build();
    }

    /**
     * 下载文件
     * @param filename
     * @return
     */
    @Override
    public byte[] download(String filename) {
        return fileStoreApi.loadFileBytes(filename);
    }

    /**
     * 获取指定时间范围内未关联业务表的文件信息
     * @param fromDate
     * @param toDate
     * @return
     */
    @Override
    public List<FileInfo> loadUnRelatedFilenames(Date fromDate, Date toDate) {
        List<FileInfo> baseFileInfos = fileInfoMapper.loadUnRelatedFilenames(fromDate, toDate);
        return baseFileInfos;
    }

    /**
     * 根据文件名获取id
     *
     * @param filename
     * @return
     */
    private String getIdByFilename(String filename) {
        Assert.hasText(filename, "文件名不能为空");
        return StringUtils.substringBefore(filename, StringConstant.DOT);
    }

    @Override
    public void unRelateByFilename(String filename) {
        try {
            fileInfoMapper.unRelate(getIdByFilename(filename));
        } catch (Exception e) {
            throw new RuntimeException("文件状态修改失败");
        }
    }

    @Override
    public void unRelateByFilenames(FileInfo[] fileInfos) {
        if (ArrayUtils.isEmpty(fileInfos)) {
            return;
        }
        try {
            fileInfoMapper.unRelateBatch(fileInfos);
        } catch (Exception e) {
            throw new RuntimeException("文件状态修改失败");
        }
    }

    @Override
    public void unRelateByFilenames(List<String> filenames) {
        if (CollectionUtils.isEmpty(filenames)) {
            return;
        }
        List<FileInfo> fileInfos = Lists.newArrayListWithExpectedSize(filenames.size());
        filenames.parallelStream().forEach(filename -> fileInfos.add(new FileInfo(filename)));
        unRelateByFilenames(fileInfos.toArray(new FileInfo[0]));
    }

    @Override
    public File loadFile(String filename) {
        return fileStoreApi.loadFile(filename);
    }
}
